package ch.jamme.impl;

import ch.jamme.*;
import ch.jamme.conf.*;

/**
 * The defualt implementation of the main interface of this project.
 * This implementation is intended to be thread-safe. That means that it can be instantiated as a singleton
 * and safely used across a multithread application.
 * @author Mario Fusco
 */
public class MarshallerImpl implements Marshaller {
	
	private final MarshallerConfiguration defaultConfiguration = new MarshallerConfiguration();

	/**
	 * Marshall an Object in its XML representation
	 * @param source The source object to be marshalled
	 * @return The String containing the XML resulting from the marshall process
	 */
	public String marshall(Object source) {
		return marshall(source, defaultConfiguration);
	}

	/**
	 * Marshall an Object in its XML representation using the given configuration.
	 * @param source The source object to be marshalled
	 * @param configuration The configuration used to marshall the given object
	 * @return The String containing the XML resulting from the marshall process
	 */
	public String marshall(Object source, MarshallerConfiguration configuration) {
		MarshallerEngine marshallerEngine = new MarshallerEngine(configuration == null ? defaultConfiguration : configuration);
		String xml = marshallerEngine.toXml(source);
		return xml;
	}
	
	/**
	 * Unmarshall an XML String transforming it in the graph of objects that it represents
	 * @param xml The String to be unmarshalled
	 * @return The Object resulting from the unmarshall process
	 */
	public Object unmarshall(String xml) {
		return unmarshall(xml, null, defaultConfiguration);
	}
	
	/**
	 * Unmarshall an XML String transforming it in the graph of objects that it represents
	 * having as a root an object of the given class
	 * @param xml The String to be unmarshalled
	 * @param rootClass The class of the object resulting from the xml unmarshall
	 * @return The Object resulting from the unmarshall process
	 */
	public <T> T unmarshall(String xml, Class<T> rootClass) {
		return unmarshall(xml, rootClass, defaultConfiguration);
	}

	/**
	 * Unmarshall an XML String transforming it in the graph of objects that it represents using the given configuration.
	 * @param xml The String to be unmarshalled
	 * @param configuration The configuration used to unmarshall the given xml string
	 * @return The Object resulting from the unmarshall process
	 */
	public Object unmarshall(String xml, MarshallerConfiguration configuration) {
		return unmarshall(xml, null, configuration);
	}
	
	/**
	 * Unmarshall an XML String transforming it in the graph of objects that it represents
	 * having as a root an object of the given class using the given configuration.
	 * @param xml The String to be unmarshalled
	 * @param rootClass The class of the object resulting from the xml unmarshall
	 * @param configuration The configuration used to unmarshall the given xml string
	 * @return The Object resulting from the unmarshall process
	 */
	public <T> T unmarshall(String xml, Class<T> rootClass, MarshallerConfiguration configuration) {
		MarshallerEngine marshallerEngine = new MarshallerEngine(configuration == null ? defaultConfiguration : configuration);
		T object = marshallerEngine.toObject(xml, rootClass);
		return object;
	}

	// ////////////////////////////////////////////////////////////////////////
	// /// Configuration
	// ////////////////////////////////////////////////////////////////////////
	
	/**
	 * Returns the default configuration used by this marshaller.
	 * The returned configuration object is a clone of the actual one, so it can be safely modified
	 * and used in a specific marshall process without changing the standard marshaller behavior.
	 */
	public MarshallerConfiguration getConfiguration() {
		return defaultConfiguration.clone();
	}
	
	/**
	 * Set to true if you want this marshaller to work in a fail safe way
	 * It means that during the marshall and unmarshall operations it will never throw an Exception,
	 * but it will just log a problem and it will try to workaround it with a best effort policy. 
	 * In this case the correctness of the result cannot be guaranteed.
	 * By default it is set to false.
	 */
	public void setFailSafe(boolean failSafe) {
		defaultConfiguration.setFailSafe(failSafe);
	}
	
	/**
	 * Sets the style used to marshall an objects' graph.
	 * By default it is set to BIDIRECTIONAL that is the most verbose but also the only one that assures 
	 * that the resulting XML could be unmarshalled back in the graph from which it has been generated.
	 */
	public void setMarshallStyle(MarshallStyle marshallStyle) {
		defaultConfiguration.setMarshallStyle(marshallStyle);
	}
	
	/**
	 * Sets the ClassNormalizer used during the marshall process.
	 * This is used to normalize the Class (i.e. the actual Class instead of the proxied one) of an object before to marshall it
	 */
	public void setClassNormalizer(ClassNormalizer classNormalizer) {
		defaultConfiguration.setClassNormalizer(classNormalizer);
	}
	
	/**
	 * Sets the ObjectToStringConvertor used during the marshall process.
	 * This is used to convert an object in its String representation and 
	 * conversely a String in the Object that it represents (given its Class)
	 */
	public void setObjectToStringConvertor(ObjectToStringConvertor objectToStringConvertor) {
		defaultConfiguration.setObjectToStringConvertor(objectToStringConvertor);
	}
	
	/**
	 * Sets the ObjectCreator used during the marshall process.
	 * This is used to define a strategy to Create an empty Object that starting from its descrption expressed as a JDOM Element
	 */
	public void setObjectCreator(ObjectCreator objectCreator) {
		defaultConfiguration.setObjectCreator(objectCreator);
	}

	/**
	 * Sets the ObjectIntrospector used during the marshall process.
	 * This is used to introspect an Object in order to transform its properties in a collection of name/value pairs
	 */
	public void setObjectIntrospector(ObjectIntrospector objectIntrospector) {
		defaultConfiguration.setObjectIntrospector(objectIntrospector);
	}

	/**
	 * Sets the FieldSuppressionStrategy used during the marshall process.
	 * This is used to define a strategy to decide if a given field should be ignored during tha marshall phase
	 */
	public void setFieldSuppressionStrategy(FieldSuppressionStrategy fieldSuppressionStrategy) {
		defaultConfiguration.setFieldSuppressionStrategy(fieldSuppressionStrategy);
	}

	/**
	 * Sets the FieldBindingStrategy used during the marshall process.
	 * This is used to define a strategy to decide if a given field should be mapped as an attribute or an element in the resulting xml
	 */
	public void setFieldBindingStrategy(FieldBindingStrategy fieldBindingStrategy) {
		defaultConfiguration.setFieldBindingStrategy(fieldBindingStrategy);
	}
	
	/**
	 * Sets the FieldPrimitiveStrategy used during the marshall process.
	 * This is used to define a strategy to decide if a given field should be treated as a primitive (single plain value) field or as a complex one
	 */
	public void setFieldPrimitiveStrategy(FieldPrimitiveStrategy fieldPrimitiveStrategy) {
		defaultConfiguration.setFieldPrimitiveStrategy(fieldPrimitiveStrategy);
	}

	/**
	 * Sets the FieldNamingStrategy used during the marshall process.
	 * This is used to define a strategy to translate a field name in the corresponding name marshalled into the xml and viceversa
	 */
	public void setFieldNamingStrategy(FieldNamingStrategy fieldNamingStrategy) {
		defaultConfiguration.setFieldNamingStrategy(fieldNamingStrategy);
	}
	
	/**
	 * Sets the MarshallerThreadStrategy used during the marshall process.
	 * This is used to define how many parallel threads should be used for a given marshall process
	 */
	public void setMarshallerThreadStrategy(MarshallerThreadStrategy marshallerThreadStrategy) {
		defaultConfiguration.setMarshallerThreadStrategy(marshallerThreadStrategy);
	}

	/**
	 * Sets the naming convention to be used while generating marshalling an object in xml
	 */
	public void setNamingConvention(NamingConvention namingConvention) {
		defaultConfiguration.setNamingConvention(namingConvention);
	}

}
